package com.wjm.shootgame.shoot;

import com.wjm.shootgame.shoot.img.Images;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Timer;

/*
* 战场类
* */
public class World extends JPanel {
    //设置游戏状态
    public static final int START=0;    //开始状态
    public static final int RUNING=1;   //运行状态
    public static final int PURSE=2;    //暂停状态
    public static final int GAMEOVER=3; //结束状态
    public int state=START;   //设置游戏初始状态为START状态

    //设置容器宽高
    public static final int WIDTH=512;
    public static final int HEIGHT=768;
    //分数
    public int score=0;

    public Hero hero = new Hero();
    public Bullet[] bullet = {};
    public  Sky sky = new Sky();

//    public BigAirplane bigAirplane;
//    public Airplane airplane;
//    public Bee bee;
    //创建敌人数组，数据类型为FlyingObject，用来存放bee/bigAirplane/airplane对象
    public FlyingObject[] enemies = {};


    /**
     * action方法
     */
    public void action(){
        //鼠标监听的方法
        mouseAction();
        //定时器方法
        timeAction();
        //键盘监听方法
        keyboradAction();

    }

    /**
     * 定时器
     */
    private void timeAction() {
        //Java中的定时器
        Timer timer = new Timer();
        //调用时间表
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //游戏状态为RUNING开始运行
                if (state == RUNING){
                    //每调用一次这个方法就存一个敌机对象到enemies[]数组中
                    enterEnemies();
                    //每调用一次这个方法就可以判断是否出界
                    outBoundAction();
                    //英雄碰撞的方法
                    heroHatAction();
                    //子弹碰撞方法
                    buttleHatAction();
                    //飞行物移动的方法
                    moveAction();
                    //检查游戏是否结束的方法
                    checkGameOver();
                }
                //刷新（重新画界面）
                repaint();
            }
        },10,10);//第一个delay（参数）100ms是表示schedule执行100毫秒后在执行run方法，后面一个period参数表示每100毫秒执行一次run方法
    }

    /**
     * 鼠标监听器
     */
    private void mouseAction() {
        //鼠标监听器对象
        MouseAdapter mouseAdapter = new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {//鼠标点击事件
                if (state == START){
                    //鼠标点击之后从start状态转为runing状态
                    state = RUNING;
                }
                if (state == RUNING){
                    //每调用一次这个方法就会有子弹存入到bullet[]数组中,然后由paint()方法将子弹画出来
                    shootAction();
                }
                if (state == GAMEOVER){
                    //游戏结束后在点击，转化到开始状态，同时清空数组，和重新创建hero对象
                    state = START;
                    enemies = new FlyingObject[0];
                    bullet = new Bullet[0];
                    hero = new Hero();
                    score = 0;
                }
            }

            @Override
            public void mouseEntered(MouseEvent e) {  //鼠标进入事件
                //如果在暂停状态时候，鼠标移出则切为运行状态
                if (state == PURSE){
                    state = RUNING;
                }
            }

            @Override
            public void mouseExited(MouseEvent e) {  //鼠标移出事件
                //如果在运行状态时候，鼠标移出则切为暂停状态
                if (state == RUNING){
                    state = PURSE;
                }
            }

            @Override
            public void mouseMoved(MouseEvent e) {//鼠标移动
                if (state == RUNING){
                    //获取鼠标坐标
                    int x = e.getX();
                    int y = e.getY();
                    //将鼠标坐标传给英雄类
                    hero.heroMove(x,y);
                }
            }
        };
        //将鼠标监听器对象加到面板上
        this.addMouseListener(mouseAdapter);
        //给当前的窗口对象添加处理鼠标移动的事件
        this.addMouseMotionListener(mouseAdapter);
    }

    /**
     * 键盘监听器
     */
    public void keyboradAction(){
        KeyListener keyListener = new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {

            }

            @Override
            public void keyPressed(KeyEvent e) {//键盘按下
                //获取键盘按下的键值
                int key = e.getKeyCode();
                //如果按下的是空格
                if (key == KeyEvent.VK_SPACE){
                    //清空敌人和子弹数组
                    bullet = new Bullet[0];
                    enemies = new FlyingObject[0];
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {//键盘释放

            }
        };
        this.addKeyListener(keyListener);
        this.requestFocus();
    }

    /**
     * 这是一个将敌机对象存入enemies[]数组的方法(扩容)
     */
    int enemyTime = 0;
    private void enterEnemies(){
        enemyTime++;
        //if语句用于控制飞机的生成速度
        if (enemyTime%40==0){//就是每10*40毫秒添加一个敌机
            //接收createEnemy（）方法传过来的对象
            FlyingObject f = createEnemy();
            //对enemies[]数组进行扩容每次长度加1
            enemies = Arrays.copyOf(enemies,enemies.length+1);
            //将获取的对象，每次都存入数组的最后一个位置
            enemies[enemies.length-1] = f;
        }
    }


    /**
     * 这是一个将英雄射出的子弹存入到bullet[]数组的方法(扩容)
     */
    private void shootAction(){
        //接收英雄类射出的子弹数组
        Bullet[] b = hero.shootBullets();
        //对bullet数据进行扩容，扩容长度为接收到的长度
        bullet = Arrays.copyOf(bullet,b.length+bullet.length);
        //将接收到的子弹数组b[]的元素复制到bullet[]数组的尾部
        //srcPos:0表示从b[]数组的0元素开始复制，desPos表示从bullet[]数组哪个位置开始放，最后一个参数表示复制的长度
        System.arraycopy(b,0,bullet,bullet.length-b.length, b.length);
    }


    /**
     * 对出界的对象数组和发生碰撞的对象进行缩容
     */
    private void outBoundAction(){
        /*
        * 对敌机数组缩容
        * */
        //创建一个于enemies[]数组等长的数组用来存放没有出界的敌机对象
        FlyingObject[] flyingObject = new FlyingObject[enemies.length];
        //可用来计算，和表示没有出界对象数组的下标
        int index = 0;
        for (int i =0;i<enemies.length;i++){
            //isOutBound()用于判断是否出界，出界返回true，没出返回false;isRemove()是用于判断当前是否为可移除状态，
            if (!enemies[i].isOutBound() && !enemies[i].isRemove()){
                flyingObject[index++] = enemies[i];
                //flyingObject[index] = enemies[i];
                //index++;
            }
        }
        //开始对enemies[]进行缩容
        enemies = Arrays.copyOf(flyingObject,index);


        /*
        * 对子弹数组缩容
        * */
        //创建一个于buttle[]数组等长的数组用来存放没有出界的敌机对象
        Bullet[] b = new Bullet[bullet.length];
        //可用来计算，和表示没有出界对象数组的下标
        int index1 = 0;
        for (int i =0;i<bullet.length;i++){
            //isOutBound()用于判断是否出界，出界返回true，没出返回false;isRemove()是用于判断当前是否为可移除状态，
            if (!bullet[i].isOutBound() && !bullet[i].isRemove()){
                b[index1++] = bullet[i];
                //flyingObject[index] = enemies[i];
                //index++;
            }
        }
        //开始对buttle[]进行缩容
        bullet = Arrays.copyOf(b,index1);
    }


    /**
     * 封装了飞行物移动的行为
     */
    private void moveAction(){
        //调用天空的move方法
        sky.move();
        //调用敌机的移动方法
        for (int i=0;i<enemies.length;i++){
            enemies[i].move();
        }
        //调用子弹的move方法
        for (int i=0;i<bullet.length;i++){
            bullet[i].move();
        }
    }


    /**
     * 这是一个生成敌机的方法
     * @return 返回敌机的对象
     */
    public FlyingObject createEnemy(){
        //生成0到20之间的随机数
        Random random = new Random();
        int a = random.nextInt(20);
        //自己规定：如果a在[0,5]则返回bee对象，如果a在[5,13]则返回airplane对象，如果a在[13,20]则返回bigAirplane对象
        if (a<=5){
            return new Bee();
        }else if (a<=13){
            return new Airplane();
        }else {
            return new BigAirplane();
        }
    }

    /**
     * 英雄碰撞的方法
     */
    public void heroHatAction(){
        for (int i=0;i<enemies.length;i++){
            FlyingObject f = enemies[i];
            //如果，敌机和英雄机都是或者的，并且发生了碰撞
            if (f.isLive() && hero.isLive() && hero.isHat(f)){
                //敌机变为死亡状态
                f.goDead();
                //英雄机生命值减1，火力值变单倍
                hero.substractLife();
                hero.clearFire();
            }
        }
    }

    /**
     * 子弹碰撞的方法
     */
    public void buttleHatAction(){
        for (int i=0;i<bullet.length;i++){
            Bullet b = bullet[i];
            for (int j=0;j<enemies.length;j++){
                FlyingObject f = enemies[j];
                if (b.isLive() && f.isLive() && b.isHat(f)){
                    b.goDead();
                    f.goDead();
                    //instanceof关键字用于判断是否属于这个接口
                    if (f instanceof Score){//如果是则一定是bigairplane和airplane
                        //需要强转f发类型之后，才能调用winScore方法
                            Score s= (Score) f;
                            score += s.winScore();
                    }else {//如果不是则是bee
                            Award  a = (Award) f;
                            int type = a.awardType();
                            switch (type){
                                //如果是双倍火力类型的则让英雄机调用增加火力的方法
                                case Award.DOUBLE_FIRE:
                                    hero.addFire();
                                    break;
                                //如果是增加生命类型的则让英雄机调用增加火力的方法
                                case Award.AWARD_LIFE:
                                    hero.addLife();
                                    break;
                        }
                    }
                }
            }
        }
    }

    /**
     * 检查游戏是否结束的方法
     */
    public void checkGameOver(){
        if (hero.getLife() == 0){
            state = GAMEOVER;
        }
    }

    /**
     * 重写父类(Jcomponent)方法,是一个画画的功能
     * @param g 相当于是画笔
     */
    @Override
    public void paint(Graphics g) {
        //画天空的图片
        sky.paintImage(g);
        //画英雄机
        hero.paintImage(g);
        //画敌机
        for (int i=0;i<enemies.length;i++){
            enemies[i].paintImage(g);
        }
        //画子弹
        for (int i=0;i<bullet.length;i++){
            bullet[i].paintImage(g);
        }
        //根据不同的状态来画不同的图片
        if (state == START){
            g.drawImage(Images.startImage,50,50,null);
        }else if (state == PURSE){
            g.drawImage(Images.purseImage,50,50,null);
        }else if (state == GAMEOVER){
            g.drawImage(Images.gameOverImage,50,50,null);
        }

        //画底部的数据
        paintData(g);

    }

    /**
     * 画底部数据的方法
     * @param g 相当于是画笔
     */
    public void paintData(Graphics g){
        //设置画笔颜色
        g.setColor(new Color(222, 195, 30, 235));
        //设置字体的格式，大小等,两种写法，第一个是字体格式，第二个是加粗，变斜等，第三个是字体大小
        g.setFont(new Font("楷体",1,20));
        //g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,18));
        //用画笔在画布上写字，注意优先级，先运行的代码在最下面，如果写在画图片代码的上面则看不见写的字
        g.drawString("SCORE:"+score,120,623);
        g.drawString("LIFE:"+hero.getLife(),120,655);
        g.drawString("AUTHOR:WJM",120,685);

        /*
         * Java中时间的表示
         *
         * */
        //创建当前系统时间，date默认是英文的且有时区
        Date date=new Date();
        //创建格式化工厂 ,默认的加工格式是22-6-5 下午2:42
        //SimpleDateFormat simpleDateFormat=new SimpleDateFormat();
        //可自定义加工模式,y表示year，yyyy表示四位数，M表示月(mouth)，m表示秒，H表示是24小时制的小时，h表示12小时制
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy/MM/dd/ HH:mm:ss");
        //将获取到的系统时间进行加工
        String time=simpleDateFormat.format(date);
        g.drawString("TIME:"+time,120,715);


        //画阴影
        g.setColor(new Color(86, 108, 219, 69));
        g.fillRect(0,600,World.WIDTH,150);
        //画头像
        g.drawImage(Images.myImage,0,600,100,120,null);

    }


    public static void main(String[] args) {
        //设置画框对象
        JFrame jFrame = new JFrame("java基础小项目-飞机大作战");
        //设置画布对象，world继承了jpanel
        World world = new World();
        //将画布添加到画框中
        jFrame.add(world);

        //设置画布的size
        jFrame.setSize(WIDTH,HEIGHT);
        //设置窗口可见性
        jFrame.setVisible(true);
        //设置窗口居中
        jFrame.setLocationRelativeTo(null);
        //设置窗口关闭方式（关闭窗口同时结束程序）
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置画面优先级（总是显示在最上面）
        jFrame.setAlwaysOnTop(true);

        world.action();
    }
}
